package com.clsz.aop;

import com.alibaba.fastjson.JSON;
import com.clsz.annotation.RedisCache;
import com.clsz.utils.RedisUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

/**
 * redis缓存策略切面类
 */
@Component
@Aspect
public class RedisCacheAspect {
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private RedissonClient redissonClient;
    /***
     * 环绕增强器
     * @param joinPoint 连接点
     * @param redisCache 缓存注解
     * @return
     */
    @Around("@annotation(redisCache)")
    public Object around(ProceedingJoinPoint joinPoint, RedisCache redisCache) {
        Map<String, Object> keyMap = new HashMap<>();
        String signature = joinPoint.getSignature().toString();
        Object[] args = joinPoint.getArgs();
        keyMap.put("method", signature);
        keyMap.put("params", args);
        String key = "Cache" + JSON.toJSONString(keyMap);
        RLock lock = redissonClient.getLock("Lock-" + key);
        try {
            Object cacheData = redisUtils.get(key);
            if (cacheData != null) {
                return cacheData.toString().equals("null") ? null : cacheData;
            }
            //缓存击穿：争夺分布式锁
            //生成锁对象
            //加分布式锁
            lock.lock();
            cacheData = redisUtils.get(key);
            if (cacheData != null) {
                return cacheData.toString().equals("null") ? null : cacheData;
            }
            Object value = joinPoint.proceed();
            //缓存穿透策略
            redisUtils.set(key, value == null ? "null" : value, value == null ? 10 : redisCache.duration());
            return value;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        } finally {
            //解锁
            if(lock.isHeldByCurrentThread()){ // 时候是当前执行线程的锁
                lock.unlock(); // 释放锁
            }
        }
    }
}